﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Linq;
using ScrobbleItSL.Data;
using ScrobbleItSL.Utils.Md5;

namespace ScrobbleItSL.LastFmApi
{
    public class LastFmSimpleClient
    {
        public const string ApiKey = "af34f77f3af135d4b28d3755965ee1f3";
        public const string Secret = "d28259a9a2995e3eba02399d4ebad612";
        public const string ApiStartUrl = @"http://ws.audioscrobbler.com/2.0/";

        public const string ApiKeyParamName = "api_key";
        public const string MethodParamName = "method";        

        public void AuthMobileSession(User user, Action success, Action<LastFmErrorEventArgs> fail )
        {
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add(MethodParamName, "auth.getMobileSession");
            param.Add(ApiKeyParamName,ApiKey);
            param.Add("username", user.Login);
            param.Add("authToken", MD5Core.GetHashString(user.Login + MD5Core.GetHashString(user.Password).ToLower()).ToLower());
            param.Add("api_sig",SignCall(param));

            string url = GetUrl(param);

            HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);             
            req.BeginGetResponse(ar => UserAuthCallback(ar, user, success, fail), req );
        }

        private void UserAuthCallback(IAsyncResult ar, User user, Action success, Action<LastFmErrorEventArgs> fail)
        {
            HttpWebRequest request = (HttpWebRequest)ar.AsyncState;

            string resultString = null;
            try
            {
                HttpWebResponse response = (HttpWebResponse) request.EndGetResponse(ar);
                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    resultString = sr.ReadToEnd();
                }
            } 
            catch(Exception ex)
            {
                fail(new LastFmErrorEventArgs(-1, ex.Message));
                return;
            }

            XDocument xdoc = XDocument.Parse(resultString);

            if (!CheckErrors(xdoc, fail))
            {
                user.UserName = xdoc.Descendants("name").First().Value;
                user.Key = xdoc.Descendants("key").First().Value;
                user.Subscriber = xdoc.Descendants("subscriber").First().Value;

                success();
            }
        }

        private string GetUrl(Dictionary<string,string> parameters)
        {
            StringBuilder sb = new StringBuilder(ApiStartUrl);
            sb.Append('?');
            foreach (KeyValuePair<string, string> keyValuePair in parameters)
            {
                sb.Append(keyValuePair.Key);
                sb.Append("=");
                sb.Append(keyValuePair.Value);
                sb.Append("&");
            }
            if (sb[sb.Length - 1].Equals('&'))
                sb.Remove(sb.Length - 1, 1);
            return sb.ToString();
        }

        private string SignCall(Dictionary<string,string> parameters)
        {
            var keys = parameters.Keys.ToList();
            keys.Sort(StringComparer.InvariantCultureIgnoreCase);
            StringBuilder sb = new StringBuilder();
            foreach (string key in keys)
            {
                sb.Append(key);
                sb.Append(parameters[key]);
            }

            sb.Append(Secret);

            return MD5Core.GetHashString(sb.ToString()).ToLower();
        }

        private bool CheckErrors(XDocument xDocument, Action<LastFmErrorEventArgs> callback)
        {
            if (xDocument.Root.Attribute("status").Value.Equals("failed", StringComparison.InvariantCultureIgnoreCase))
            {
                var error = xDocument.Element("error");
                LastFmErrorEventArgs args = new LastFmErrorEventArgs(Convert.ToInt32(error.Attribute("code").Value), error.Value);
                callback(args);
                return true;
            }
            return false;
        }
    }
}
